package network

import (
	"context"
	"crypto/tls"
	"net"
	"net/http"

	"gitee.com/doraemon1996/bunny/goroutine"
	"gitee.com/doraemon1996/bunny/log"
	"gitee.com/doraemon1996/bunny/uuid"
	"github.com/gorilla/websocket"
)

// Weboskcet连接请求处理器
type WSHandler struct {
	http.ServeMux

	ln        *WSListener // Websocket监听器
	onConnect OnConnect   // 新连接处理回调

	Upgrader *websocket.Upgrader
}

// 新建Weboskcet连接请求处理器
func newWSHandler(ln *WSListener, onConnect OnConnect) *WSHandler {
	return &WSHandler{
		ln:        ln,
		onConnect: onConnect,
		Upgrader: &websocket.Upgrader{
			// 允许任意来源的连接请求
			CheckOrigin: func(r *http.Request) bool { return true },
		},
	}
}

func (handler *WSHandler) accept(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	if handler.ln.ctl.Closed() {
		http.Error(w, "HTTP server is closed(code:500-11).", 500)
		return
	}

	rawConn, err := handler.Upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Errorf("Upgrader %#v failed. error: %+v", r, err)
		return
	}

	isTLS := false
	if handler.ln.Type() == Listener_WSS {
		isTLS = true
	}

	wsConn := NewWSConn(handler.ln, rawConn, isTLS)
	handler.onConnect(wsConn)
}

// Websocket监听器
type WSListener struct {
	listener

	path    string           // 请求路径
	tlsCfg  *tls.Config      // 加密通信配置
	rawLn   *net.TCPListener // 底层监听器
	httpSrv *http.Server     // HTTP服务
}

// 新建Websocket监听器
func NewWSListener(ctx context.Context, network, address, path string) (ln *WSListener, err Error) {
	addr, underlyingErr := net.ResolveTCPAddr("tcp", address)
	if underlyingErr != nil {
		err = &networkError{ErrorType_Underlying, underlyingErr}
		return
	}

	rawLn, underlyingErr := net.ListenTCP("tcp", addr)
	if underlyingErr != nil {
		err = &networkError{ErrorType_Underlying, underlyingErr}
		return
	}

	ln = new(WSListener)
	ln.id = uuid.GenUint64()
	ln.network = network
	ln.address = address
	ln.path = path
	ln.rawLn = rawLn
	ln.ctl.Init(ctx, nil)

	return
}

// 新建Websocket监听器 TLS
func NewWSSListener(ctx context.Context, network, address, path string, tlsCfg *tls.Config) (ln *WSListener, err Error) {
	if tlsCfg == nil {
		err = &networkError{ErrorType_Network, ErrorTLSConfigIsNil}
		return
	}

	addr, underlyingErr := net.ResolveTCPAddr("tcp", address)
	if underlyingErr != nil {
		err = &networkError{ErrorType_Underlying, underlyingErr}
		return
	}

	rawLn, underlyingErr := net.ListenTCP("tcp", addr)
	if underlyingErr != nil {
		err = &networkError{ErrorType_Underlying, underlyingErr}
		return
	}

	ln = new(WSListener)
	ln.id = uuid.GenUint64()
	ln.network = network
	ln.address = address
	ln.path = path
	ln.tlsCfg = tlsCfg
	ln.rawLn = rawLn
	ln.ctl.Init(ctx, nil)

	return
}

// 获取监听器类型
func (ln *WSListener) Type() ListenerType {
	if ln.network == "wss" {
		return Listener_WSS
	} else {
		return Listener_WS
	}
}

// 获取底层监听器
func (ln *WSListener) RawListener() any {
	return ln.rawLn
}

// 开始运行监听器的循环逻辑
func (ln *WSListener) Start(f OnConnect) {
	ln.ctl.Start(func() {
		ln.ctl.Add(1)
		goroutine.Submit(func() {
			defer ln.ctl.Done()
			ln.serve(f)
		})
	})
}

// 停止并关闭监听器
// 使用监听器了Context或派生Context的地方都在影响范围内
func (ln *WSListener) Stop() {
	ln.ctl.Stop(func() {
		log.Infof("%v is about to exit", ln)

		if ln.httpSrv != nil {
			subCtx, cancel := context.WithCancel(ln.ctl.Context())
			if err := ln.httpSrv.Shutdown(subCtx); err != nil {
				log.Errorf("WSListener httpSrv[%s] shutdown failed. error: %v", ln.httpSrv.Addr, err)
			}
			cancel()
		} else if ln.rawLn != nil {
			if err := ln.rawLn.Close(); err != nil {
				log.Errorf("WSListener close underlying listener catch error: %v", err)
			}
		}

		ln.ctl.Wait()
	})
}

// 启动http服务
func (ln *WSListener) serve(f OnConnect) {
	handler := newWSHandler(ln, f)
	handler.HandleFunc(ln.path, handler.accept)
	ln.httpSrv = &http.Server{
		Addr:    ln.rawLn.Addr().String(),
		Handler: handler,
	}

	if ln.tlsCfg != nil {
		l := tls.NewListener(ln.rawLn, ln.tlsCfg)
		err := ln.httpSrv.Serve(l)
		if ln.ctl.Opening() {
			log.Warnf("WSSListener underlying HttpSrv accept catch error: %v", err)
		}
	} else {
		err := ln.httpSrv.Serve(ln.rawLn)
		if ln.ctl.Opening() {
			log.Warnf("WSListener underlying HttpSrv accept catch error: %v", err)
		}
	}
}
